#!/usr/bin/perl

#===============================================================================
#     REVISION:  $Id$
#  DESCRIPTION:  Common case scenario test: register, login, set appartment info,
#                add/delete meterings, fill blank, setup notifications etc.
#===============================================================================

use strict;
use warnings;

use Readonly;
Readonly our $VERSION => qw($Revision$) [1];

use English qw( -no_match_vars );
use DateTime;

#use Smart::Comments;

#$ENV{'DONT_CLEANUP'} = 1;

use Meter::Test qw(
    init_apache
    get_test_webapp_url
);
use Meter::Test::WWW qw(
    init_houses
    get_action_link_regex
    u8
    get_username
    get_password
    get_new_password
    get_house_address
    get_last_name
    get_first_name
    get_middle_name
    get_cold_water_meter_number
    get_hot_water_meter_number
    get_appartment_number
    get_cold_water_consumption
    get_hot_water_consumption
    get_metering_passing_day
    get_email
);

use Test::WWW::Mechanize;
use Test::More;

Readonly my $NUMBER_OF_USERS       => 10;
Readonly my $NUMBER_OF_TESTS       => 87;
Readonly my $NUMBER_OF_EXTRA_TESTS => 4;

sub _welcome {
    my $mech       = shift;
    my $user_index = shift;

    $mech->get_ok( get_test_webapp_url() );
    $mech->content_contains('WELCOME_PAGE_MARKER');

    return;
}

sub _register {
    my $mech       = shift;
    my $user_index = shift;

    my $username = get_username($user_index);
    my $password = get_password($user_index);

    $mech->follow_link_ok( { url_regex => get_action_link_regex('register') },
        'Go to register page' );
    $mech->content_contains('REGISTER_PAGE_MARKER');
    $mech->submit_form_ok(
        {   form_name => 'register_form',
            fields    => {
                username       => $username,
                password       => $password,
                password_again => $password,
            },
        },
        "Register user '$username'"
    );
    $mech->content_contains('RESULT_OK_PAGE_MARKER');

    return;
}

sub _login {
    my $mech       = shift;
    my $user_index = shift;
    my $password   = shift || get_password($user_index);

    my $username = get_username($user_index);

    $mech->follow_link_ok( { url_regex => get_action_link_regex('login') },
        'Go to login page' );
    $mech->content_contains('LOGIN_PAGE_MARKER');
    $mech->submit_form_ok(
        {   form_name => 'login_form',
            fields    => {
                username => $username,
                password => $password,
            },
        },
        "Login as '$username'"
    );
    $mech->content_contains('RESULT_OK_PAGE_MARKER');

    return;
}

sub _set_appartment_info {
    my $mech       = shift;
    my $user_index = shift;

    my $username = get_username($user_index);
    $mech->follow_link_ok(
        { url_regex => get_action_link_regex('appartment_info') },
        'Go to appartment info page' );
    $mech->content_contains('APPARTMENT_INFO_PAGE_MARKER');

    $mech->form_name('appartment_info_form');
    $mech->select( 'house_id', u8( get_house_address($user_index) ) );
    $mech->submit_form_ok(
        {   form_name => 'appartment_info_form',
            fields    => {
                appartment_number => get_appartment_number($user_index),
                last_name         => u8( get_last_name($user_index) ),
                first_name        => u8( get_first_name($user_index) ),
                middle_name       => u8( get_middle_name($user_index) ),
                cold_water_meter_number =>
                    get_cold_water_meter_number($user_index),
                hot_water_meter_number =>
                    get_hot_water_meter_number($user_index),
            },
        },
        "Set appartment info for '$username'"
    );

    $mech->content_contains('RESULT_OK_PAGE_MARKER');

    return;
}

sub _get_previous_year {
    return DateTime->now()->subtract( years => 1 )->year();
}

sub _add_meterings {
    my $mech       = shift;
    my $user_index = shift;

    my $username = get_username($user_index);

    my $year = _get_previous_year();
    my $day  = get_metering_passing_day();

    my $cold_water_end_amount = 0;
    my $hot_water_end_amount  = 0;

    foreach my $month ( 1 .. 11 ) {    ## no critic (ProhibitMagicNumbers)
        $mech->follow_link_ok(
            { url_regex => get_action_link_regex('add_metering') },
            'Go to add metering page' );

        $cold_water_end_amount
            += get_cold_water_consumption( $user_index, $month );
        $hot_water_end_amount
            += get_hot_water_consumption( $user_index, $month );

        if ( $month == 1 ) {
            $mech->submit_form_ok(
                {   form_name => 'add_metering_form',
                    fields    => {
                        start_date =>
                            sprintf( '%04d-%02d-%02d', $year, $month, $day ),
                        end_date => sprintf( '%04d-%02d-%02d',
                            $year, $month + 1, $day ),
                        cold_water_start_amount => 0,
                        cold_water_end_amount   => $cold_water_end_amount,
                        hot_water_start_amount  => 0,
                        hot_water_end_amount    => $hot_water_end_amount,
                    },
                },
                "Add metering for user '$username', month $month"
            );

        }
        else {    # almost all fields should be pre-filled
            $mech->submit_form_ok(
                {   form_name => 'add_metering_form',
                    fields    => {
                        end_date => sprintf( '%04d-%02d-%02d',
                            $year, $month + 1, $day ),
                        cold_water_end_amount => $cold_water_end_amount,
                        hot_water_end_amount  => $hot_water_end_amount,
                    },
                },
                "Add metering for user '$username', month $month"
            );
        }

        $mech->content_contains('RESULT_OK_PAGE_MARKER');
    }

    $mech->follow_link_ok(
        { url_regex => get_action_link_regex('show_meterings') },
        "Go to show meterings page to check metering totals of '$username'"
    );

    $mech->content_contains( "+$cold_water_end_amount",
        "Check total cold water amount is +$cold_water_end_amount" );
    $mech->content_contains( "+$hot_water_end_amount",
        "Check total hot water amount is +$hot_water_end_amount" );

    return;
}

sub _fill_blank {
    my $mech       = shift;
    my $user_index = shift;

    my $username = get_username($user_index);

    $mech->follow_link_ok(
        { url_regex => get_action_link_regex('show_meterings') },
        "Go to show meterings page to fill blank for '$username'"
    );
    $mech->content_contains('SHOW_METERINGS_PAGE_MARKER');

    my $metering_id = _get_the_most_recent_metering_id($mech);

    # submit 1st 'fill_blank' form on the page
    $mech->submit_form_ok( { form_number => 2 },
        "Filling blank for user '$username', metering_id: $metering_id" );

    $mech->content_contains('FILL_BLANK_PAGE_MARKER');

    my $fio = get_last_name($user_index) . q{ } . get_first_name($user_index);
    if ( get_middle_name($user_index) ) {
        $fio .= q{ } . get_middle_name($user_index);
    }

    my $fio_text = "Собственник $fio";
    $mech->text_contains( u8($fio_text),
        "Check that blank contains '$fio_text'" );

    my $address_text = 'Адрес: ' . get_house_address($user_index);
    $mech->text_contains( u8($address_text),
        "Check that blank contains '$address_text'" );

    # test adds 11 meterings (Jan..Dec), and fills the most recent blank
    my $year = _get_previous_year();
    my $day  = get_metering_passing_day();

    my $metering_date_text = qq{На "$day" декабря $year г.};
    $mech->text_contains( u8($metering_date_text),
        "Check that blank contains '$metering_date_text'" );

    my $metering_prev_date_text
        = qq{Предыдущие показания "$day" ноября $year г.};
    $mech->text_contains( u8($metering_prev_date_text),
        "Check that blank contains '$metering_prev_date_text'" );

    my $cold_water_meter_number = get_cold_water_meter_number($user_index);
    $mech->text_contains( u8($cold_water_meter_number),
        "Check that blank contains cold water meter number '$cold_water_meter_number'"
    );

    my $hot_water_meter_number = get_hot_water_meter_number($user_index);
    $mech->text_contains( u8($hot_water_meter_number),
        "Check that blank contains hot water meter number '$hot_water_meter_number'"
    );

    # TODO check start/end cold/hot water meter amounts

    $mech->back();    # blank is opened on a new page

    return;
}

sub _get_the_most_recent_metering_id {
    my $mech = shift;

    my $form
        = $mech->form_number(2);    # select 1st 'fill_blank' form on the page
    my ($metering_id_input) = $mech->find_all_inputs(
        type => 'hidden',
        name => 'metering_id',
    );

    return $metering_id_input->value();
}

sub _delete_metering {
    my $mech       = shift;
    my $user_index = shift;

    my $username = get_username($user_index);

    $mech->follow_link_ok(
        { url_regex => get_action_link_regex('show_meterings') },
        "Go to show meterings page to delete most recent metering for '$username'"
    );
    $mech->content_contains('SHOW_METERINGS_PAGE_MARKER');

    my $metering_id = _get_the_most_recent_metering_id($mech);

    # submit 1st 'delete_metering' form on the page
    $mech->submit_form_ok(
        { form_number => 1 },
        "Deleting metering with metering_id: $metering_id for user '$username'"
    );
    $mech->content_contains('RESULT_OK_PAGE_MARKER');

    $mech->follow_link_ok(
        { url_regex => get_action_link_regex('show_meterings') },
        "Go to show meterings page to check that metering with metering_id: $metering_id was deleted"
    );
    $mech->content_contains('SHOW_METERINGS_PAGE_MARKER');
    my $new_metering_id = _get_the_most_recent_metering_id($mech);
    isnt( $new_metering_id, $metering_id,
        "Check that there is no more metering with metering_id: $metering_id"
    );

    return;
}

sub _setup_notifications {
    my $mech       = shift;
    my $user_index = shift;

    my $username = get_username($user_index);

    $mech->follow_link_ok(
        { url_regex => get_action_link_regex('setup_notifications') },
        "Go to setup_notifications page for '$username'"
    );
    $mech->content_contains('SETUP_NOTIFICATIONS_PAGE_MARKER');

    my $email = get_email($user_index);
    $mech->submit_form_ok(
        {   form_name => 'setup_notifications_form',
            fields    => { email => $email },
        },
        "Set email of '$username' to '$email'"
    );
    $mech->content_contains('RESULT_OK_PAGE_MARKER');

    $mech->follow_link_ok(
        { url_regex => get_action_link_regex('setup_notifications') },
        "Go to setup_notifications page for '$username' again to check email"
    );

    $mech->content_contains( $email, "Check that '$email' was saved" );

    return;
}

sub _change_password {
    my $mech       = shift;
    my $user_index = shift;

    my $username = get_username($user_index);

    $mech->follow_link_ok(
        { url_regex => get_action_link_regex('change_password') },
        "Go to change_password page for '$username'" );
    $mech->content_contains('CHANGE_PASSWORD_PAGE_MARKER');

    my $new_password = get_new_password($user_index);
    $mech->submit_form_ok(
        {   form_name => 'change_password_form',
            fields    => {
                password       => $new_password,
                password_again => $new_password,
            },
        },
        "Change password of user '$username' to '$new_password'"
    );
    $mech->content_contains('RESULT_OK_PAGE_MARKER');

    return;
}

sub _logout {
    my $mech       = shift;
    my $user_index = shift;

    my $username = get_username($user_index);

    $mech->follow_link_ok( { url_regex => get_action_link_regex('logout') },
        "Logout '$username'" );
    $mech->content_contains('RESULT_OK_PAGE_MARKER');

    return;
}

sub _check_stats {
    my $mech = Test::WWW::Mechanize->new();

    $mech->get_ok( get_test_webapp_url() );

    # NOTE: extra space before numbers!
    my $users_registered
        = "Зарегистрировано пользователей:  $NUMBER_OF_USERS";
    $mech->text_contains( u8($users_registered),
        "Check stats contains '$users_registered'" );

    # all of them are loged out at this moment
    my $users_online = 'Из них сейчас на сайте:  0';
    $mech->text_contains( u8($users_online),
        "Check that stats contains '$users_online'" );

    ## no critic (ProhibitMagicNumbers)
    my $meterings_registred
        = 'Количество зарегистрированых показаний:  '
        . $NUMBER_OF_USERS * 10;

    $mech->text_contains( u8($meterings_registred),
        "Check that stats contains '$meterings_registred'" );

    return;
}

sub _delete_accont {
    my $mech       = shift;
    my $user_index = shift;

    my $username = get_username($user_index);

    $mech->follow_link_ok(
        { url_regex => get_action_link_regex('delete_account') },
        "Delete account of '$username'" );
    $mech->content_contains('RESULT_OK_PAGE_MARKER');

    return;
}

sub test_common_case {

    # shuffle user activity to make test more realistic
    my @user_sessions = ();
    foreach my $user_index ( 1 .. $NUMBER_OF_USERS ) {
        my $mech = Test::WWW::Mechanize->new();

        push @user_sessions,
            {
            user_index => $user_index,
            mech       => $mech,
            steps      => [
                \&_welcome,         \&_register,
                \&_login,           \&_set_appartment_info,
                \&_add_meterings,   \&_fill_blank,
                \&_delete_metering, \&_setup_notifications,
                \&_change_password, \&_logout,
            ],
            };
    }

    my $total_steps
        = $NUMBER_OF_USERS * scalar @{ $user_sessions[0]->{'steps'} };

USER_SESSION:
    while ( $total_steps > 0 ) {
        my $rand_user_session = $user_sessions[ int rand $NUMBER_OF_USERS ];

        if ( !@{ $rand_user_session->{'steps'} } ) {
            next USER_SESSION;
        }

        my $step = shift @{ $rand_user_session->{'steps'} };
        $step->(
            $rand_user_session->{'mech'},
            $rand_user_session->{'user_index'},
        );

        $total_steps--;
    }

    _check_stats();

    foreach my $user_index ( 1 .. $NUMBER_OF_USERS ) {
        my $mech = Test::WWW::Mechanize->new();

        _welcome( $mech, $user_index );
        _login( $mech, $user_index, get_new_password($user_index) );
        _delete_accont( $mech, $user_index );
    }

    return;
}

sub set_test_plan {
    plan tests => $NUMBER_OF_TESTS * $NUMBER_OF_USERS
        + $NUMBER_OF_EXTRA_TESTS;

    return;
}

sub run_tests {
    set_test_plan();
    init_apache();
    init_houses();
    test_common_case();

    return;
}

run_tests();
